package com.example.util;

import cn.hutool.core.util.NumberUtil;
import lombok.Data;
import oshi.SystemInfo;
import oshi.hardware.*;
import oshi.hardware.CentralProcessor.TickType;
import oshi.software.os.FileSystem;
import oshi.software.os.NetworkParams;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yzh
 * @version 1.0
 * @date 2021/7/26 18:05
 */
public class SystemInfoUtils {
    private static final SystemInfo SYSTEM_INFO = new SystemInfo();
    private static final HardwareAbstractionLayer HAL = SYSTEM_INFO.getHardware();
    private static final int OSHI_WAIT_SECOND = 1000;

    private SystemInfoUtils() {
    }

    @Data
    private static class Cpu {
        /**
         * 物理 CPU 核心数
         */
        private int cpuNum;
        /**
         * CPU 总的使用率
         */
        private double total;
        /**
         * CPU 系统使用率
         */
        private double sys;
        /**
         * CPU 用户使用率
         */
        private double used;
        /**
         * CPU 当前等待率
         */
        private double wait;
        /**
         * CPU 当前空闲率
         */
        private double free;

        public void setTotal(double total) {
            // round 保留两位小数
            this.total = NumberUtil.round(NumberUtil.mul(total, 100), 2).doubleValue();
        }

        public void setSys(double sys) {
            this.sys = NumberUtil.round(NumberUtil.mul(sys / total, 100), 2).doubleValue();
        }

        public void setUsed(double used) {
            this.used = NumberUtil.round(NumberUtil.mul(used / total, 100), 2).doubleValue();
        }

        public void setWait(double wait) {
            this.wait = NumberUtil.round(NumberUtil.mul(wait / total, 100), 2).doubleValue();
        }

        public void setFree(double free) {
            this.free = NumberUtil.round(NumberUtil.mul(free / total, 100), 2).doubleValue();
        }

    }

    @Data
    private static class Mem {
        /**
         * 内存总量
         */
        private double total;
        /**
         * 已用内存
         */
        private double used;
        /**
         * 剩余内存
         */
        private double free;
        /**
         * 内存使用率
         */
        private double usage;
        /**
         * 虚拟总内存
         */
        private double virtualTotal;
        /**
         * 使用的虚拟内存
         */
        private double virtualUsed;

        /**
         * total byte
         *
         * @param total
         */
        public void setTotal(double total) {
            this.total = sizeByteToGb(total);
        }

        public void setUsed(double used) {
            this.used = sizeByteToGb(used);
        }

        public void setFree(double free) {
            this.free = sizeByteToGb(free);
        }

        public void setUsage() {
            this.usage = NumberUtil.mul(NumberUtil.div(used, total, 4), 100);
        }

        public void setVirtualTotal(double virtualTotal) {
            this.virtualTotal = sizeByteToGb(virtualTotal);
        }

        public void setVirtualUsed(double virtualUsed) {
            this.virtualUsed = sizeByteToGb(virtualUsed);
        }
    }

    @Data
    private static class OsInfo {
        /**
         * 操作系统名称
         */
        private String osName;
        /**
         * OS 供应商名称
         */
        private String manufacturer;
        /**
         * 操作系统版本
         */
        private String osVersion;
        /**
         * 处理器(CPU) 型号
         */
        private String cpuType;
        /**
         * 内存大小 GB
         */
        private double memStore;
        /**
         * 默认 IPV4 地址
         */
        private String defaultIpv4;
        /**
         * 默认 IPV6 地址
         */
        private String defaultIpv6;
        /**
         * 主机名
         */
        private String hostName;

        /**
         * 获取总内存 GB
         *
         * @param total
         */
        public void setMemStore(double total) {
            this.memStore = sizeByteToGb(total);
        }

    }

    @Data
    private static class Disk {
        /**
         * 总空间
         */
        private double total;
        /**
         * 空闲空间
         */
        private double free;
        /**
         * 使用率
         */
        private double usage;
        /**
         * 文件信息(windows 下是分盘的，Linux 不知道会咋样)???
         */
        List<FileStore> fileStores;

        public void setTotal(long total) {
            this.total = sizeByteToGb(total);
        }

        public void setFree(long free) {
            this.free = sizeByteToGb(free);
        }

        public void setUsage() {
            this.usage = 100 - NumberUtil.mul(NumberUtil.div(free, total, 4), 100);
        }

    }
    @Data
    private static class FileStore {
        /**
         * 磁盘名称
         */
        private String name;
        /**
         * 文件集(相当于挂载点？？)
         */
        private String volume;
        /**
         * 类型
         */
        private String type;
        /**
         * 总容量
         */
        private double total;
        /**
         * 可用容量
         */
        private double free;
        /**
         * 使用率 %
         */
        private double usage;

        public void setTotal(long total) {
            this.total = sizeByteToGb(total);
        }

        public void setFree(long free) {
            this.free = sizeByteToGb(free);
        }

        public void setUsage() {
            this.usage = 100 - NumberUtil.mul(NumberUtil.div(free, total, 4), 100);
        }

    }

    public static Cpu getCpu() {
        CentralProcessor processor = HAL.getProcessor();
        Cpu cpu = new Cpu();
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        Util.sleep(OSHI_WAIT_SECOND);
        long[] ticks = processor.getSystemCpuLoadTicks();
        long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
        long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
        long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
        long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
        long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
        long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
        long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
        long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
        long total = user + nice + cSys + idle + iowait + irq + softirq + steal;
        cpu.setCpuNum(processor.getPhysicalProcessorCount());
        cpu.setTotal(total);
        cpu.setSys(cSys);
        cpu.setUsed(user);
        cpu.setWait(iowait);
        cpu.setFree(idle);
        return cpu;
    }

    public static Mem getMemory() {
        GlobalMemory memory = HAL.getMemory();
        Mem mem = new Mem();
        mem.setTotal(memory.getTotal());
        mem.setUsed(NumberUtil.sub(memory.getTotal(), memory.getAvailable()));
        mem.setFree(memory.getAvailable());
        mem.setUsage();
        mem.setVirtualTotal(memory.getSwapTotal());
        mem.setVirtualUsed(memory.getSwapUsed());
        return mem;
    }

    public static OsInfo getOsInfo() {
        OperatingSystem os = SYSTEM_INFO.getOperatingSystem();
        OsInfo osInfo = new OsInfo();
        osInfo.setOsName(os.getFamily());
        osInfo.setManufacturer(os.getManufacturer());
        osInfo.setOsVersion(os.getVersion().getVersion());
        CentralProcessor processor = HAL.getProcessor();
        osInfo.setCpuType(processor.getName());
        GlobalMemory memory = HAL.getMemory();
        osInfo.setMemStore(memory.getTotal());
        NetworkParams networkParams = os.getNetworkParams();
        osInfo.setHostName(networkParams.getHostName());
        osInfo.setDefaultIpv4(networkParams.getIpv4DefaultGateway());
        osInfo.setDefaultIpv6(networkParams.getIpv6DefaultGateway());
        return osInfo;
    }

    public static Disk getDisk() {
        OperatingSystem operatingSystem = SYSTEM_INFO.getOperatingSystem();
        FileSystem fileSystem = operatingSystem.getFileSystem();
        Disk disk = new Disk();
        long diskTotalSize = 0;
        long diskFreeSize = 0;
        OSFileStore[] fileStores = fileSystem.getFileStores();
        int len = fileStores.length;
        List<FileStore> list = new ArrayList<>(len);
        for (int i = 0; (null != fileStores) && (i < len); i++) {
            OSFileStore fileStore = fileStores[i];
            long storeTotalSpace = fileStore.getTotalSpace();
            diskTotalSize += storeTotalSpace;
            long storeFreeSpace = fileStore.getUsableSpace();
            diskFreeSize += storeFreeSpace;
            FileStore myFileStore = new FileStore();
            myFileStore.setName(fileStore.getName());
            myFileStore.setType(fileStore.getType());
            myFileStore.setVolume(fileStore.getVolume());
            myFileStore.setTotal(storeTotalSpace);
            myFileStore.setFree(storeFreeSpace);
            myFileStore.setUsage();
            list.add(myFileStore);
        }
        disk.setTotal(diskTotalSize);
        disk.setFree(diskFreeSize);
        disk.setFileStores(list);
        disk.setUsage();
        return disk;
    }

    private static double sizeByteToGb(double size) {
        return NumberUtil.div(size, 1024 * 1024 * 1024, 2);
    }

    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        try {
            rt.exec("cmd /c dir");
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(SystemInfoUtils.getOsInfo());
    }

}
